package pw.http.proxy;

import okhttp3.*;
import pw.http.proxy.core.ICallback;
import pw.http.proxy.core.IHandleCallback;
import pw.http.proxy.okhttp3.ProgressListener;
import pw.http.proxy.okhttp3.ProgressRequestBody;
import pw.http.proxy.utils.IdKit;

import java.io.*;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author Eric
 * @Desc
 * @Date Create in 18-1-11 下午3:01
 * @Modified By
 */
public class HttpProxy {
    public static boolean DEBUG = false;
    private static final Map<Object,HttpProxy> requests = Collections.synchronizedMap(new HashMap<Object,HttpProxy>());
    private static OkHttpClient okHttpClient ;
    private Type type = Type.GET;
    private String url;
    private Map<String,Object> params = new HashMap();
    private Map<String,Object> header = new HashMap();
    private String savePath;
    private ProgressListener progressListener;
    private boolean sync = true;
    private Call call ;// 供撤销处理句柄
    private String flag ;// remove的flag key
    enum Type{
        GET,POST,UPLOAD,DOWNLOAD
    }

    public static void  init(final HttpConfig config){
        okHttpClient=new OkHttpClient.Builder()
                .cookieJar(new CookieJar() {
                    private final HashMap<String, List<Cookie>> cookieStore = new HashMap<String, List<Cookie>>();

                    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
                        cookieStore.put(url.host(), cookies);
                        if(config.getCookieListener() != null){
                            if(cookies != null){
                                Map<String,String> cs = new HashMap<String, String>();
                                for(Cookie cookie : cookies){
                                    cs.put(cookie.name(),cookie.value());
                                }
                                config.getCookieListener().saveCookie(url.host(),url.toString(),cs);
                            }
                        }
                    }

                    public List<Cookie> loadForRequest(HttpUrl url) {
                        ArrayList list = new ArrayList<Cookie>();
                        if(config.getCookieListener() != null){
                            Map<String,String> cookies = config.getCookieListener().loadCookie(url.host(),url.toString());
                            if(cookies != null){
                                Iterator<Map.Entry<String,String>> iterator = cookies.entrySet().iterator();
                                while (iterator.hasNext()){
                                    Map.Entry<String,String> entry = iterator.next();
                                    list.add(new Cookie.Builder().domain("jorm").name(entry.getKey()).value(entry.getValue()).build());
                                }
                            }
                        }
                        return list;
                    }
                })
                .connectTimeout(config.getConnectTimeout(), TimeUnit.MILLISECONDS)
                .readTimeout(config.getReadTimeout(), TimeUnit.MILLISECONDS)
                .build();
    }

    /**
     * 创建一个Get请求
     * @param url
     * @return
     */
    public static HttpProxy get(String url){
        HttpProxy proxy1 = new HttpProxy();
        proxy1.url = url;
        proxy1.type = Type.GET;
        return proxy1;
    }

    /**
     * 创建一个Post请求
     * @param url
     * @return
     */
    public static HttpProxy post(String url){
        HttpProxy proxy1 = new HttpProxy();
        proxy1.url = url;
        proxy1.type = Type.POST;
        return proxy1;
    }

    /**
     * 上传文件请求
     * @param url
     * @param listener 上传文件的进入的跟踪
     * @return
     */
    public static HttpProxy upload(String url, ProgressListener listener){
        HttpProxy proxy1 = new HttpProxy();
        proxy1.url = url;
        proxy1.type = Type.UPLOAD;
        proxy1.progressListener = listener;
        return proxy1;
    }

    /**
     * 下载文件请求
     * @param url
     * @param listener 下载文件的进入的跟踪
     * @return
     */
    public static HttpProxy download(String url, ProgressListener listener){
        HttpProxy proxy1 = new HttpProxy();
        proxy1.url = url;
        proxy1.type = Type.DOWNLOAD;
        proxy1.progressListener = listener;
        return proxy1;
    }

    /**
     * 增加请求参数，Get请求会做url拼接，Post会作为消息体的一部分
     * @param key
     * @param value
     * @return
     */
    public HttpProxy addParam(String key, Object value){
        params.put(key,value);
        return this;
    }

    /**
     * 批量增加请求参数，Get请求会做url拼接，Post会作为消息体的一部分
     * @param params
     * @return
     */
    public HttpProxy addParams(Map params){
        params.putAll(params);
        return this;
    }

    /**
     * 只有在download的情况下才有效，置顶保存文件的路径
     * @param localPath
     * @return
     */
    public HttpProxy saveTo(String localPath) {
        this.savePath = localPath;
        return this;
    }

    /**
     * 添加请求头
     * @param key
     * @param value
     * @return
     */
    public HttpProxy addHeader(String key, Object value) {
        this.header.put(key, value);
        return this;
    }

    /**
     * 批量添加请求头
     * @param headers
     * @return
     */
    public HttpProxy addHeaders(Map<String, Object> headers) {
        this.header.putAll(headers);
        return this;
    }

    /**
     * 异步处理，返回标记码
     * @param callback
     * @return 唯一标识码flag 可以通过 HttpProxy.cancel(flag) 来取消当前的请求
     */
    public Object request(final ICallback callback) throws IOException {
        sync = true;
        return request0(callback);
    }

    /**
     * 异步处理，返回标记码
     * @return 唯一标识码flag 可以通过 HttpProxy.cancel(flag) 来取消当前的请求
     */
    public Object request() throws IOException {
        sync = true;
        return request0(null);
    }

    /**
     * 执行同步请求
     * 默认返回的是未处理byte[]
     * @return
     */
    public Object requestAsyn() throws IOException {
        sync = false;
        return request0(null);
    }

    private Object request0(final ICallback callback) throws IOException{
        String realUrl = url;
        Request.Builder requestBuilder = new Request.Builder();
        switch (type){
            case GET:
                realUrl = concatGetUrl();
                requestBuilder.url(realUrl);
                break;
            case POST:
                requestBuilder.url(realUrl);
                if (params != null && params.size() > 0) {
                    FormBody.Builder formBodyBuilder = new FormBody.Builder();
                    Set<Map.Entry<String, Object>> entrySet = params.entrySet();
                    for (Map.Entry<String, Object> entry : entrySet) {
                        formBodyBuilder.add(entry.getKey(), entry.getValue().toString());
                    }
                    requestBuilder.post(formBodyBuilder.build());
                }
                break;
            case UPLOAD:
                requestBuilder.url(realUrl);
                MultipartBody.Builder builder = new MultipartBody.Builder();
                //设置类型
                builder.setType(MultipartBody.FORM);
                //追加参数
                for (String key : params.keySet()) {
                    Object object = params.get(key);
                    if (!(object instanceof File)) {
                        builder.addFormDataPart(key, object.toString());
                    } else {
                        File file = (File) object;
                        builder.addFormDataPart(key, file.getName(), RequestBody.create(null, file));
                    }
                }
                if(progressListener != null){
                    requestBuilder.post(new ProgressRequestBody(builder.build(),progressListener));
                }else{
                    requestBuilder.post(builder.build());
                }

                break;
            case DOWNLOAD:
                realUrl = concatGetUrl();
                requestBuilder.url(realUrl);
                break;
        }
        if(DEBUG){
            System.out.println("request url :"+realUrl);
        }

        Request request = requestBuilder.build();

        Call call = okHttpClient.newCall(request);

        if(sync){
            call.enqueue(new Callback() {
                public void onFailure(Call call, IOException e) {
                    removeFromTaskList();
                    if(callback == null) return;
                    callback.onFinish();
                    callback.onFail(-1,e);
                }

                public void onResponse(Call call, Response response) throws IOException {
                    removeFromTaskList();
                    if(callback != null){
                        callback.onFinish();
                    }
                    if(!response.isSuccessful()){ // 处理非 200 的状态码
                        if(callback == null) return;
                        callback.onFail(response.code(),null);
                        return ;
                    }
                    if(type == Type.DOWNLOAD){
                        ResponseBody body = response.body() ;
                        long total = body.contentLength();
                        InputStream inputStream = body.byteStream();
                        byte[]bytes = new byte[1024];
                        int length = 0,currBytes = 0 ;
                        File file = new File(savePath);
                        FileOutputStream outputStream = new FileOutputStream(file);
                        while ((length = inputStream.read(bytes)) != -1){
                            outputStream.write(bytes,0,length);
                            currBytes += length;
                            if(progressListener != null)
                                progressListener.onProgress(currBytes,total,currBytes == total);
                        }
                        outputStream.close();
                        inputStream.close();
                    }else{
                        if(callback == null) return;
                        String respStr = response.body().string();
                        if(callback instanceof IHandleCallback){
                            callback.onSuccess(((IHandleCallback) callback).handler(respStr));
                        }else{
                            callback.onSuccess(respStr);
                        }
                    }
                }
            });
            this.call = call;
            this.flag = IdKit.md5(realUrl);
            requests.put(flag,this);
            return flag;
        }else{
            byte[] res = call.execute().body().bytes();
            callback.onFinish();
            return res;
        }
    }

    void removeFromTaskList(){
        requests.remove(flag);
    }

    void canel(){
        if(call != null){
            call.cancel();
        }
    }

    private String concatGetUrl() throws UnsupportedEncodingException {
        String realUrl = url;
        StringBuilder sb = new StringBuilder();
        if (params != null && params.size() > 0) {
            Set<Map.Entry<String, Object>> entrySet = params.entrySet();
            if(!realUrl.contains("?")){
                sb.append("?");
            }
            if(realUrl.endsWith("&")){
                realUrl = realUrl.substring(0,realUrl.length()-2);
            }

            for (Map.Entry<String, Object> entry : entrySet) {
                sb.append(entry.getKey());
                sb.append("=");
                sb.append(URLEncoder.encode(entry.getValue().toString(), "UTF-8"));
                sb.append("&");
            }
            sb.deleteCharAt(sb.length() - 1);
        }
        if(sb.length() > 0){
            return realUrl + sb.toString();
        }
        return realUrl;
    }

    /**
     * 取消某次请求，如果还没有处理完成的话
     * @param flag
     */
    public static final void cancel(Object flag){
        HttpProxy httpProxy = requests.get(flag);
        if(httpProxy != null){
            httpProxy.canel();
            requests.remove(flag);
        }
    }
}
